/**
 * CommunicationHandler.java
 * ************************** 
 * @date Mar 22, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package networkRmi;


import java.net.InetAddress;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Queue;

import messages.Message;
import messages.MessageHandler;

/**
 * 
 * Description:
 * The CommunicationHandler is a NetworkManager, so it's able to give
 * the access to another node through the Java RMI.
 * Because different services may need the call of the underlying net, 
 * our approach consists of two objects:
 * a CommunicationServer and a MessageDispatcher.
 * The CommunicationServer performs remote calls, while the 
 * MessageDispatcher uses the MessageHandler of each registered service.
 * 
 * With this approach a service that need the RMI communication need first
 * to register to the NetworkManager providing its own MessageHandler 
 * in order to receive Message(s).
 * Then it can take advantage of the send() function. 
 *  
 *
 */
public class CommunicationHandler implements NetworkManager {
	
	private static boolean SHOW_ERRORS = false;
	
	/**
	 * The 'receivedMessages' is a List containing all incoming messages
	 * (of all services) 
	 * collected through the CommunicationServer.
	 * WARNING: data shared by two components: 
	 * CommunicationServer and MessageHandler.
	 */
	private static Deque<Message> receivedMessages = new ArrayDeque<Message>(500);
	
	/**
	 * Once the MessageDispatcher is called, it moves all the Message(s) from
	 * the 'receivedMessages' to the 'messagesToDispatch'.
	 * Because we have to ensure thread safe operations, the MessageDispatcher 
	 * will operate on the 'messagesToDispatch' list, leaving the 
	 * 'receivedMessages' ready for new incoming Messages.
	 * WARNING: data shared by two components: 
	 * CommunicationServer and MessageHandler.
	 */
	private static Queue<Message> messagesToDispatch = new ArrayDeque<Message>(500);
	
	private CommunicationServer comServer = CommunicationServer.getComunicationServer(receivedMessages);
	private MessageDispatcher msgDispatcher = new MessageDispatcher(receivedMessages, messagesToDispatch);
	
	/**
	 * Is the name of the node. It's used during the communication between 
	 * two nodes. A node is registered into the local rmiregistry with this name.
	 * Recall that a machine may host more than a node. 
	 */
	protected String nodeName;
	
	/**
	 * This constructor will start the CommunicationServer and the 
	 * MessageDispatcher.
	 * 
	 * @param nodeName : String
	 */
	public CommunicationHandler(String nodeName){
		super();
		this.nodeName = nodeName;
		startCommunicationHandler(nodeName);
	}
	                            

	/**
	 * Returns the communicationServer which provides the remote
	 * interface implementation.
	 * 
	 * @return comServer : CommunicationServer
	 */
	public final CommunicationServer getComServer() {
		return comServer;
	}

	/**
	 * Returns the MessageDispatcher.
	 * 
	 * @return msgDispatcher : MessageDispatcher
	 */
	public final MessageDispatcher getMsgDispatcher() {
		return msgDispatcher;
	}

	protected void startCommunicationHandler(String serviceName) {
		try {
			comServer.registryToRmiregistry(serviceName);
			msgDispatcher.start();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		System.out.println("Node is up registered with service name: "+serviceName);		
	}

	@Override
	public void registerService(MessageHandler handler) {
		msgDispatcher.register(handler);
	}
	
	@Override
	public void deRegisterService(MessageHandler handler){
		msgDispatcher.deRegister(handler);
	}


	@Override
	public void send(Message msg, InetAddress destHost) throws NotBoundException, RemoteException {
//		try {		
			if( (comServer.sendMessage(msg, destHost) == false) && SHOW_ERRORS){
				System.err.println("Problem in sending message "+msg+" to "+
						msg.getDest().getNodeAddress().getHostAddress());
			}
//		} catch (RemoteException e) {
//			e.printStackTrace();
//		}
	}
	
	@Override
	public final String getNodeName() {
		return nodeName;
	}
	
	public void enableShowErrors(){
		SHOW_ERRORS = true;
	}
	
	public void disableShowErrors(){
		SHOW_ERRORS = false;
	}


	@Override
	public Class<?> getServiceClass() {
		return NetworkManager.class;
	}


	@Override
	public boolean ping(InetAddress node, String service) 
		throws RemoteException, NotBoundException {
		return comServer.ping(node, service);
	}

}
